#pragma once

#include <string>
#include <vector>
#include <fstream>
#include <unordered_map>
#include <unordered_set>
#include <sstream>
#include <unordered_set>

#include "../../Utility/Log/Log.hpp"
#include "../../Utility/Static/Algorithmf.hpp"
#include "../../Utility/Data/NavMessageData/GNSSMesData.hpp"

namespace ns_GNSSMesInput
{
    using namespace ns_GNSSMesData;

    class GNSSMesHeaderDecoder
    {
    private:
        static const int HeaderPreSize = 6;
        static const int IonoCorrSize = 7;
        static const int TimeCorrSize = 7;
        static const int LeapSecondSize = 4;

    public:
        GNSSMesHeaderDecoder()
        {
        }

        static int HeaderPreDecode(const std::vector<std::string> &input, GNSSMesHeader::Ptr *output)
        {
            GNSSMesHeader::Ptr header = std::make_shared<GNSSMesHeader>();

            if (input.size() != HeaderPreSize)
                return -1;

            header->version = input[0];

            *output = header;

            return 0;
        }

        static int IonoCorrDecode(const std::vector<std::string> &input, GNSSMesHeader::Ptr *output)
        {
            if (input.size() != IonoCorrSize)
                return -1;

            GNSSMesHeader::Ptr header = std::make_shared<GNSSMesHeader>();

            GNSSType gnssType = GNSSTypeUtil::GetGNSSTypeByName(input[0]);
            if (gnssType == GNSSType::DefaultGNSSType)
            {
                return -1;
            }

            header->gnssType = gnssType;

            double ins[4];
            for (int i = 0; i < 4; i++)
            {
                ins[i] = Algorithmf::StringToDouble(input[i + 1]);
            }

            if (input[0][3] == 'A')
            {
                memcpy(header->ionAlpha, ins, sizeof(double) * 4);
            }
            else if (input[0][3] == 'B')
            {
                memcpy(header->ionBeta, ins, sizeof(double) * 4);
            }

            *output = header;

            return 0;
        }

        static int TimeCorrDecode(const std::vector<std::string> &input, GNSSMesHeader::Ptr *output)
        {
            if (input.size() != TimeCorrSize)
                return -1;

            GNSSMesHeader::Ptr header = std::make_shared<GNSSMesHeader>();

            GNSSType gnssType = GNSSTypeUtil::GetGNSSTypeByName(input[0]);
            if (gnssType == GNSSType::DefaultGNSSType)
            {
                return -1;
            }

            header->gnssType = gnssType;

            double ins[4];
            for (int i = 0; i < 4; i++)
            {
                ins[i] = Algorithmf::StringToDouble(input[i + 1]);
            }

            memcpy(&(header->timeSysCorr), ins, sizeof(double) * 4);

            *output = header;

            return 0;
        }

        static int LeapSecondDecode(const std::vector<std::string> &input, GNSSMesHeader::Ptr *output)
        {
            if (input.size() != LeapSecondSize)
                return -1;

            GNSSMesHeader::Ptr header = std::make_shared<GNSSMesHeader>();

            header->leapSecond = Algorithmf::StringToDouble(input[0]);

            *output = header;

            return 0;
        }
    };

    class GNSSMesRecordDecoder
    {
        static const int Bit324Size = 22;
        static const int Bit648Size = 37;

    public:
        GNSSMesRecordDecoder()
        {
        }

        static int Decode(const std::vector<std::string> &input, GNSSMesRecord::Ptr *output)
        {
            GNSSType markType = GNSSTypeUtil::GetGNSSTypeByMark(input[0]);
            switch (markType)
            {
            case GNSSType::BDS:
                return DecodeFrom648Bit(input, output);

            case GNSSType::Galileo:
                return DecodeFrom648Bit(input, output);

            case GNSSType::GLONASS:
                return DecodeFrom324Bit(input, output);

            case GNSSType::GPS:
                return DecodeFrom648Bit(input, output);

            case GNSSType::IRNSS:
                return DecodeFrom648Bit(input, output);

            case GNSSType::QZSS:
                return DecodeFrom648Bit(input, output);

            case GNSSType::SBSA:
                return DecodeFrom324Bit(input, output);
            default:
                break;
            }

            return -1;
        }

    private:
        static int DecodeFrom324Bit(const std::vector<std::string> &input, GNSSMesRecord::Ptr *output)
        {
            if (input.size() != Bit324Size)
                return DecodeError;

            GNSSType markType = GNSSTypeUtil::GetGNSSTypeByMark(input[0]);
            if (markType == GNSSType::DefaultGNSSType)
            {
                return DecodeError;
            }

            GNSSMesRecord *ins = new GNSSMesRecord();
            ins->gnssType = markType;
            ins->serial = Algorithmf::StringToInt(input[1]);

            double *tmp = &(ins->year);
            for (int i = 0; i < Bit324Size - 2; i++)
            {
                *(tmp + i) = Algorithmf::StringToDouble(input[i + 2]);
            }

            output->reset(ins);

            return 0;
        }

        static int DecodeFrom648Bit(const std::vector<std::string> &input, GNSSMesRecord::Ptr *output)
        {
            if (input.size() != Bit648Size)
                return DecodeError;

            GNSSType markType = GNSSTypeUtil::GetGNSSTypeByMark(input[0]);
            if (markType == GNSSType::DefaultGNSSType)
            {
                return DecodeError;
            }

            GNSSMesRecord *ins = new GNSSMesRecord();
            ins->gnssType = markType;
            ins->serial = Algorithmf::StringToInt(input[1]);

            double *tmp = &(ins->year);
            for (int i = 0; i < Bit648Size - 2; i++)
            {
                *(tmp + i) = Algorithmf::StringToDouble(input[i + 2]);
            }

            output->reset(ins);

            return 0;
        }
    };
}